home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1995 #5 & #6 / Amiga Plus CD - 1995 - No. 5 and 6.iso / pd / daten / astrolog / src / general.c < prev    next >
C/C++ Source or Header  |  1995-08-11  |  30KB  |  1,212 lines

  1. /*                                                               -*- C -*-
  2. ** Astrolog (Version 4.40) File: general.c
  3. **
  4. ** IMPORTANT NOTICE: The graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
  6. ** (astara@u.washington.edu). Permission is granted to freely use and
  7. ** distribute these routines provided one doesn't sell, restrict, or
  8. ** profit from them in any way. Modification is allowed provided these
  9. ** notices remain with any altered or edited versions of the program.
  10. **
  11. ** The main planetary calculation routines used in this program have
  12. ** been Copyrighted and the core of this program is basically a
  13. ** conversion to C of the routines created by James Neely as listed in
  14. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  15. ** available from Matrix Software. The copyright gives us permission to
  16. ** use the routines for personal use but not to sell them or profit from
  17. ** them in any way.
  18. **
  19. ** The PostScript code within the core graphics routines are programmed
  20. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  21. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  22. **
  23. ** The extended accurate ephemeris databases and formulas are from the
  24. ** calculation routines in the program "Placalc" and are programmed and
  25. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  26. ** (alois@azur.ch). The use of that source code is subject to
  27. ** regulations made by Astrodienst Zurich, and the code is not in the
  28. ** public domain. This copyright notice must not be changed or removed
  29. ** by any user of this program.
  30. **
  31. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  32. ** X Window graphics initially programmed 10/23-29/1991.
  33. ** PostScript graphics initially programmed 11/29-30/1992.
  34. ** Last code change made 1/29/1995.
  35. */
  36.  
  37. /* $VER: $Id: general.c,v 1.2 1995/07/02 22:21:37 tf Exp $ */
  38.  
  39. #include "astrolog.h"
  40.  
  41.  
  42. /*
  43. ******************************************************************************
  44. ** General Procedures.
  45. ******************************************************************************
  46. */
  47.  
  48. /* Swap two floating point values. */
  49.  
  50. void SwapR(d1, d2)
  51. real *d1, *d2;
  52. {
  53.   real temp;
  54.  
  55.   temp = *d1; *d1 = *d2; *d2 = temp;
  56. }
  57.  
  58.  
  59. /* Return the length of a string (not counting the null terminator). */
  60.  
  61. int CchSz(sz)
  62. CONST char *sz;
  63. {
  64.   int i;
  65.  
  66.   for (i = 0; *sz++; i++)
  67.     ;
  68.   return i;
  69. }
  70.  
  71.  
  72. /* Compare two strings. Return 0 if they are equal, a positive value if  */
  73. /* the first string is greater, and a negative if the second is greater. */
  74.  
  75. int NCompareSz(s1, s2)
  76. CONST char *s1, *s2;
  77. {
  78.   while (*s1 && *s1 == *s2)
  79.     s1++, s2++;
  80.   return *s1 - *s2;
  81. }
  82.  
  83.  
  84. /* Set a given number of bytes to zero given a starting pointer. */
  85.  
  86. void ClearB(pb, cb)
  87. lpbyte pb;
  88. int cb;
  89. {
  90.   while (cb-- > 0)
  91.     *pb++ = 0;
  92. }
  93.  
  94.  
  95. /* Copy a given number of bytes from one location to another. */
  96.  
  97. void CopyRgb(pbSrc, pbDst, cb)
  98. byte *pbSrc, *pbDst;
  99. int cb;
  100. {
  101.   while (cb-- > 0)
  102.     *pbDst++ = *pbSrc++;
  103. }
  104.  
  105.  
  106. /* Determine the sign of a number: -1 if value negative, +1 if value */
  107. /* positive, and 0 if it's zero.                                     */
  108.  
  109. real RSgn(r)
  110. real r;
  111. {
  112.   return r == 0.0 ? 0.0 : RSgn2(r);
  113. }
  114.  
  115.  
  116. /* Given an x and y coordinate, return the angle formed by a line from the */
  117. /* origin to this coordinate. This is just converting from rectangular to  */
  118. /* polar coordinates; however, we don't determine the radius here.         */
  119.  
  120. real Angle(x, y)
  121. real x, y;
  122. {
  123.   real a;
  124.  
  125.   if (x != 0.0) 
  126.   {
  127.     if (y != 0.0)
  128.       a = RAtn(y/x);
  129.     else
  130.       a = x < 0.0 ? rPi : 0.0;
  131.   } 
  132.   else a = y < 0.0 ? -rPiHalf : rPiHalf;
  133.  
  134.   if (a < 0.0)
  135.     a += rPi;
  136.  
  137.   if (y < 0.0)
  138.     a += rPi;
  139.  
  140.   return a;
  141. }
  142.  
  143.  
  144. /* Modulus function for floating point values, where we bring the given */
  145. /* parameter to within the range of 0 to 360.                           */
  146.  
  147. real Mod(d)
  148. real d;
  149. {
  150.   if (d >= rDegMax)        /* In most cases, our value is only slightly */
  151.     d -= rDegMax;          /* out of range, so we can test for it and   */
  152.  
  153.   else if (d < 0.0)        /* avoid the more complicated arithmetic.    */
  154.     d += rDegMax;
  155.  
  156.   if (d >= 0 && d < rDegMax)
  157.     return d;
  158.  
  159.   return (d - RFloor(d/rDegMax)*rDegMax);
  160. }
  161.  
  162.  
  163. /* Another modulus function, this time for the range of 0 to 2 Pi. */
  164.  
  165. real ModRad(r)
  166. real r;
  167. {
  168.   while (r >= rPi2)    /* We assume our value is only slightly out of       */
  169.     r -= rPi2;         /* range, so test and never do any complicated math. */
  170.   while (r < 0.0)
  171.     r += rPi2;
  172.   return r;
  173. }
  174.  
  175.  
  176. /* Integer division - like the "/" operator but always rounds result down. */
  177.  
  178. long Dvd(x, y)
  179. long x, y;
  180. {
  181.   long z;
  182.  
  183.   if (y == 0)
  184.     return x;
  185.  
  186.   z = x / y;
  187.  
  188.   if (((x >= 0) == (y >= 0)) || x-z*y == 0)
  189.     return z;
  190.  
  191.   return z - 1;
  192. }
  193.  
  194.  
  195. /*
  196. ******************************************************************************
  197. ** General Astrology Procedures.
  198. ******************************************************************************
  199. */
  200.  
  201. /* A similar modulus function: convert an integer to value from 1..12. */
  202.  
  203. int Mod12(i)
  204. int i;
  205. {
  206.   while (i > cSign)
  207.     i -= cSign;
  208.  
  209.   while (i < 1)
  210.     i += cSign;
  211.  
  212.   return i;
  213. }
  214.  
  215.  
  216. /* Convert an inputed fractional degrees/minutes value to a true decimal   */
  217. /* degree quantity. For example, the user enters the decimal value "10.30" */
  218. /* to mean 10 degrees and 30 minutes; this will return 10.5, i.e. 10       */
  219. /* degrees and 30 minutes expressed as a floating point degree value.      */
  220.  
  221. real DecToDeg(d)
  222. real d;
  223. {
  224.   return RSgn(d)*(RFloor(RAbs(d))+RFract(RAbs(d))*100.0/60.0);
  225. }
  226.  
  227.  
  228. /* This is the inverse of the above function. Given a true decimal value */
  229. /* for a zodiac degree, adjust it so the degrees are in the integer part */
  230. /* and the minute expressed as hundredths, e.g. 10.5 degrees -> 10.30    */
  231.  
  232. real DegToDec(d)
  233. real d;
  234. {
  235.   return RSgn(d)*(RFloor(RAbs(d))+RFract(RAbs(d))*60.0/100.0);
  236. }
  237.  
  238.  
  239. /* Return the shortest distance between two degrees in the zodiac. This is  */
  240. /* normally their difference, but we have to check if near the Aries point. */
  241.  
  242. real MinDistance(deg1, deg2)
  243. real deg1, deg2;
  244. {
  245.   real i;
  246.  
  247.   i = RAbs(deg1-deg2);
  248.   return i < rDegHalf ? i : rDegMax - i;
  249. }
  250.  
  251.  
  252. /* This is just like the above routine, except the min distance value  */
  253. /* returned will either be positive or negative based on whether the   */
  254. /* second value is ahead or behind the first one in a circular zodiac. */
  255.  
  256. real MinDifference(deg1, deg2)
  257. real deg1, deg2;
  258. {
  259.   real i;
  260.  
  261.   i = deg2 - deg1;
  262.  
  263.   if (RAbs(i) < rDegHalf)
  264.     return i;
  265.  
  266.   return RSgn(i)*(RAbs(i) - rDegMax);
  267. }
  268.  
  269.  
  270. /* Return the degree of the midpoint between two zodiac positions, making */
  271. /* sure we return the true midpoint closest to the positions in question. */
  272.  
  273. real Midpoint(deg1, deg2)
  274. real deg1, deg2;
  275. {
  276.   real mid;
  277.  
  278.   mid = (deg1+deg2)/2.0;
  279.   return MinDistance(deg1, mid) < rDegQuad ? mid : Mod(mid+rDegHalf);
  280. }
  281.  
  282.  
  283. /* Given a planet and sign, determine whether: The planet rules the sign, */
  284. /* the planet has its fall in the sign, the planet exalts in the sign, or */
  285. /* is debilitated in the sign; and return an appropriate character.       */
  286.  
  287. char Dignify(obj, sign)
  288. int obj, sign;
  289. {
  290.   if (obj > oNorm)
  291.     return ' ';
  292.  
  293.   if (ruler1[obj] == sign || ruler2[obj] == sign)
  294.     return 'R';
  295.  
  296.   if (ruler1[obj] == Mod12(sign+6) || ruler2[obj] == Mod12(sign+6))
  297.     return 'F';
  298.  
  299.   if (exalt[obj] == sign)
  300.     return 'e';
  301.  
  302.   if (exalt[obj] == Mod12(sign+6))
  303.     return 'd';
  304.  
  305.   return '-';
  306. }
  307.  
  308.  
  309. /* Determine the number of days in a particular month. The year is needed, */
  310. /* too, because we have to check for leap years in the case of February.   */
  311.  
  312. int DayInMonth(month, year)
  313. int month, year;
  314. {
  315.   int d;
  316.  
  317.   if (month == mSep || month == mApr || month == mJun || month == mNov)
  318.     d = 30;
  319.  
  320.   else if (month != mFeb)
  321.     d = 31;
  322.  
  323.   else 
  324.   {
  325.     d = 28;
  326.     if( ((year % 4) == 0) && ((year % 100) != 0 || (year % 400) == 0 || year <= yeaJ2G))
  327.       d++;
  328.   }
  329.   return d;
  330. }
  331.  
  332.  
  333. /* Return the actual number of days in a particular month. Normally, this  */
  334. /* is the same as the above routine which determines the index of the last */
  335. /* day of the month, but the values can differ when changing between       */
  336. /* calendar systems (Julian to Gregorian) in which one can jump over days. */
  337.  
  338. int DaysInMonth(month, year)
  339. int month, year;
  340. {
  341.   int d;
  342.  
  343.   d= DayInMonth(month, year);
  344.  
  345.   if( (year == yeaJ2G) && (month == monJ2G) )
  346.     d -= (dayJ2G2 - dayJ2G1 - 1);
  347.  
  348.   return d;
  349. }
  350.  
  351.  
  352. /* Return the day of the week (Sunday is 0) of the specified given date. */
  353.  
  354. int DayOfWeek(month, day, year)
  355. int month, day, year;
  356. {
  357.   int d;
  358.  
  359.   d = (int)((MdyToJulian(month, day, year) + 1) % 7);
  360.   return d < 0 ? d+7 : d;
  361. }
  362.  
  363.  
  364. /* Given a day, and the month and year it falls in, add a number of days    */
  365. /* to it and return the new day index. As month changes are not checked for */
  366. /* here, this is mostly just adding the offset to the day; however we need  */
  367. /* to check for calendar changes for when days in a month may be skipped.   */
  368.  
  369. int AddDay(month, day, year, delta)
  370. int month, day, year, delta;
  371. {
  372.   int d;
  373.  
  374.   d = day + delta;
  375.   if( (year == yeaJ2G) && (month == monJ2G) )  /* Check for Julian to  */
  376.   {
  377.     if(d > dayJ2G1 && d < dayJ2G2)             /* Gregorian crossover. */
  378.       d += NSgn(delta)*(dayJ2G2-dayJ2G1-1);
  379.   }
  380.   return d;
  381. }
  382.  
  383.  
  384. /* Given an aspect and two objects making that aspect with each other,   */
  385. /* return the maximum orb allowed for such an aspect. Normally this only */
  386. /* depends on the aspect itself, but some objects require narrow orbs,   */
  387. /* and some allow wider orbs, so check for these cases.                  */
  388.  
  389. real GetOrb(obj1, obj2, asp)
  390. int obj1, obj2, asp;
  391. {
  392.   real orb, i;
  393.  
  394.   orb = aspectorb[asp];
  395.   i = (obj1 > oNorm) ? 2.0 : planetorb[obj1];
  396.  
  397.   orb = Min(orb, i);
  398.   i = (obj2 > oNorm) ? 2.0 : planetorb[obj2];
  399.  
  400.   orb = Min(orb, i);
  401.  
  402.   if (obj1 <= oNorm)
  403.     orb += planetadd[obj1];
  404.  
  405.   if (obj2 <= oNorm)
  406.     orb += planetadd[obj2];
  407.  
  408.   return orb;
  409. }
  410.  
  411.  
  412. /*
  413. ******************************************************************************
  414. ** String Procedures.
  415. ******************************************************************************
  416. */
  417.  
  418. /* Exit the program, and do any cleanup necessary. Note that if we had     */
  419. /* a non-fatal error, and we are in the -Q loop mode, then we won't        */
  420. /* actually terminate the program, but drop back to the command line loop. */
  421.  
  422. void Terminate(tc)
  423. int tc;
  424. {
  425.   char sz[cchSzDef];
  426.  
  427.   if (tc == tcForce)
  428.   {
  429.     S = stdout;
  430.     AnsiColor(kWhite);
  431.     sprintf(sz, "\n%s %s exited.\n", szAppName, szVersionCore);
  432.     PrintSz(sz);
  433.   }
  434.  
  435.   if (tc == tcError && us.fLoop)
  436.     return;
  437.  
  438.   if (us.fAnsi) 
  439.   {
  440.     sprintf(sz, "%c[0m", chEscape);    /* Get out of any Ansi color mode. */
  441.     PrintSz(sz);
  442.   }
  443.  
  444. #ifdef AMIGA
  445.   /* Unlike on other systems we must take care here that all graphics are    */
  446.   /* disposed since we allow altering the switches in graphics mode as well, */
  447.   /* and Terminate() might be called with an EndX() pending.                 */
  448.  
  449.   if(gi.win)
  450.     EndX();
  451. #endif /* AMIGA */
  452.  
  453.   exit(abs(tc));
  454. }
  455.  
  456.  
  457. /* Print a string on the screen. A seemingly simple operation, however we */
  458. /* keep track of what column we are printing at after each newline so we  */
  459. /* can automatically clip at the appropriate point, and we keep track of  */
  460. /* the row we are printing at, so we may prompt before screen scrolling.  */
  461.  
  462. void PrintSz(sz)
  463. CONST char *sz;
  464. {
  465.   char szInput[cchSzDef], *pch;
  466.   int nT;
  467.  
  468. #ifdef AMIGA
  469.   if(gi.win) { xPutString(sz); return; }
  470. #endif /* AMIGA */
  471.  
  472.   for (pch = (char *)sz; *pch; pch++)
  473.   {
  474.     if (*pch != '\n') 
  475.     {
  476.       is.cchCol++;
  477.       if (us.fClip80 && is.cchCol >= us.nScreenWidth)  /* Clip if need be. */
  478.         continue;
  479.     }
  480.     else 
  481.     {
  482.       is.cchRow++;
  483.       is.cchCol = 0;
  484.     }
  485.     putc(*pch, S);
  486.  
  487.     if (*pch == '\n' && us.nScrollRow > 0 && is.cchRow >= us.nScrollRow && S == stdout) 
  488.     {
  489.       /* If we've printed 'n' rows, stop and wait for a line to be entered. */
  490.  
  491.       nT = us.fAnsi;
  492.       us.fAnsi = 0;
  493.  
  494.       fflush(S);  /* added by tf */
  495.       InputString("Press return to continue scrolling", szInput);
  496.       us.fAnsi = nT;
  497.       is.cchRow = 0;
  498.  
  499.       /* One can actually give a few simple commands before hitting return. */
  500.  
  501.       if (szInput[0] == '.' || szInput[0] == 'q')
  502.         Terminate(tcForce);
  503.       else if (szInput[0] == '8')
  504.         not(us.fClip80);
  505.       else if (szInput[0] == 'Q')
  506.         us.nScrollRow = 0;
  507.       else if (szInput[0] == 'k') 
  508.       {
  509.         if (us.fAnsi)
  510.           AnsiColor(kDefault);
  511.         not(us.fAnsi);
  512.       }
  513.     }
  514.   }
  515.   fflush(S); /* added by tf */
  516. }
  517.  
  518.  
  519. /* Print a single character on the screen. */
  520.  
  521. void PrintCh(ch)
  522. char ch;
  523. {
  524.   char sz[2];
  525.  
  526.   sz[0] = ch; sz[1] = chNull;    /* Treat char as a string of length one. */
  527.   PrintSz(sz);                   /* Then call above to print the string.  */
  528. }
  529.  
  530.  
  531. /* Print a string on the screen. Unlike the normal PrintSz(), here we still */
  532. /* go to the standard output even if text is being sent to a file with -os. */
  533.  
  534. void PrintSzScreen(sz)
  535. char *sz;
  536. {
  537.   FILE *fileT;
  538.  
  539.   fileT = S;
  540.   S = stdout;
  541.   PrintSz(sz);
  542.   S = fileT;
  543. }
  544.  
  545.  
  546. /* Print a general user message given a string. This is just like the */
  547. /* warning displayer below just that we print in a different color.   */
  548.  
  549. void PrintNotice(sz)
  550. char *sz;
  551. {
  552.   AnsiColor(kYellow);
  553.  
  554. #ifdef AMIGA
  555.   if(gi.win) xPrint("%s\n",sz);
  556.   else
  557. #endif /* AMIGA */
  558.  
  559.   fprintf(stderr, "%s\n", sz);
  560.   AnsiColor(kDefault); 
  561. }
  562.  
  563.  
  564. /* Print a warning message given a string. This is called in non-fatal  */
  565. /* cases where we return to normal execution after printing the string. */
  566.  
  567. void PrintWarning(sz)
  568. char *sz;
  569. {
  570.   AnsiColor(kRed);
  571. #ifdef AMIGA
  572.   if(gi.win) xPrint("%s\n",sz);
  573.   else
  574. #endif /* AMIGA */
  575.   fprintf(stderr, "%s\n", sz);
  576.   AnsiColor(kDefault); 
  577. }
  578.  
  579.  
  580. /* Print an error message. This is called in more serious cases which halt */
  581. /* running of the current chart sequence, which can terminate the program  */
  582. /* but isn't a fatal error in that we can still fall back to the -Q loop.  */
  583.  
  584. void PrintError(sz)
  585. char *sz;
  586. {
  587.   AnsiColor(kRed);
  588. #ifdef AMIGA
  589.   if(gi.win) xPrint("%s: %s\n", szAppName, sz);
  590.   else
  591. #endif /* AMIGA */
  592.   fprintf(stderr, "%s: %s\n", szAppName, sz);
  593.   Terminate(tcError);
  594.   AnsiColor(kDefault); 
  595. }
  596.  
  597.  
  598. /* Simplification for a commonly printed error message. */
  599.  
  600. void ErrorArgc(szOpt)
  601. char *szOpt;
  602. {
  603.   char sz[cchSzDef];
  604.  
  605.   sprintf(sz, "Too few options to switch %c%s", chSwitch, szOpt);
  606.   PrintError(sz);
  607. }
  608.  
  609.  
  610. /* Another simplification for a commonly printed error message. */
  611.  
  612. void ErrorValN(szOpt, nVal)
  613. char *szOpt;
  614. int nVal;
  615. {
  616.   char sz[cchSzDef];
  617.  
  618.   sprintf(sz, "Value %d passed to switch %c%s out of range.\n", nVal, chSwitch, szOpt);
  619.   PrintError(sz);
  620. }
  621.  
  622.  
  623. /* Yet another place to print a type of error message. */
  624.  
  625. void ErrorArgv(szOpt)
  626. char *szOpt;
  627. {
  628.   char sz[cchSzDef];
  629.  
  630.   sprintf(sz, "The switch %c%s is not allowed now.\n", chSwitch, szOpt);
  631.   PrintError(sz);
  632. }
  633.  
  634.  
  635. /* Still another place to print a type of error message. */
  636.  
  637. void ErrorSwitch(szOpt)
  638. char *szOpt;
  639. {
  640.   char sz[cchSzDef];
  641.  
  642.   sprintf(sz, "Unknown switch '%s'", szOpt);
  643.   PrintError(sz);
  644. }
  645.  
  646.  
  647. /* A simple procedure used throughout Astrolog: Print a particular */
  648. /* character on the screen 'n' times.                              */
  649.  
  650. void PrintTab(ch, cch)
  651. char ch;
  652. int cch;
  653. {
  654.   int i;
  655.  
  656.   for (i = 0; i < cch; i++)
  657.     PrintCh(ch);
  658. }
  659.  
  660.  
  661. /* Set an Ansi text color. */
  662.  
  663. void AnsiColor(k)
  664. int k;
  665. {
  666.   char sz[cchSzDef];
  667.   int cchSav;
  668.  
  669. #ifdef AMIGA
  670.   if(gi.win) { DrawColor(k); return; }
  671. #endif /* AMIGA */
  672.  
  673.   /* Special case: If we are passed the value Reverse, and ansi is not    */
  674.   /* only on but set to a value > 1, then we'll enter reverse video mode. */
  675.  
  676.   if (!us.fAnsi || (k == kReverse && us.fAnsi < 2))
  677.     return;
  678.  
  679.   cchSav = is.cchCol;
  680.   is.cchCol = 0;
  681.   sprintf(sz, "%c[", chEscape);
  682.   PrintSz(sz);
  683.  
  684.   if (k == kDefault)
  685.     PrintCh('0');
  686.  
  687.   else if (k == kReverse) 
  688.   {
  689.     PrintCh('7');
  690.   } 
  691.   else
  692.   {
  693.     sprintf(sz, "%c;%d", k > 7 ? '1' : '0', 30 + (k & 7));
  694.     PrintSz(sz);
  695.   }
  696.   PrintCh('m');
  697.   is.cchCol = cchSav;
  698. }
  699.  
  700.  
  701. /* Print a zodiac position on the screen. This basically just prints the */
  702. /* string returned from SzZodiac() below, except we take care of color.  */
  703.  
  704. void PrintZodiac(deg)
  705. real deg;
  706. {
  707.   AnsiColor(kElemA[(int)(deg / 30.0) & 3]);
  708.   PrintSz(SzZodiac(deg));
  709.   AnsiColor(kDefault);
  710. }
  711.  
  712.  
  713. /* Given a zodiac position, return a string containing it as it's */
  714. /* formatted for display to the user.                             */
  715.  
  716. char *SzZodiac(deg)
  717. real deg;
  718. {
  719.   static char zod[11];
  720.   int sign, d, m;
  721.   real s;
  722.  
  723.   switch (us.nDegForm) {
  724.  
  725.   case 0:
  726.     /* Normally, we format the position in degrees/sign/minutes format: */
  727.  
  728.     deg = Mod(deg + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0));
  729.     sign = (int)floor(deg / 30.0);
  730.     d = (int)deg - sign*30;
  731.     m = (int)(RFract(deg)*60.0);
  732.     sprintf(zod, "%2d%c%c%c%02d", d, chSig3(sign + 1), m);
  733.     if (is.fSeconds) {
  734.       s = RFract(deg)*60.0; s = RFract(s)*60.0;
  735.       sprintf(&zod[7], "'%02d\"", (int)s);
  736.     }
  737.     break;
  738.  
  739.   case 1:
  740.     /* However, if -sh switch in effect, get position in hours/minutes: */
  741.  
  742.     deg = Mod(deg + (is.fSeconds ? rRound/4.0/60.0 : rRound/4.0));
  743.     d = (int)(deg / 15.0);
  744.     m = (int)((deg - (real)d*15.0)*4.0);
  745.     sprintf(zod, "%2dh,%02dm", d, m);
  746.     if (is.fSeconds) {
  747.       s = RFract(deg)*4.0; s = RFract(s)*60.0;
  748.       sprintf(&zod[7], ",%02ds", (int)s);
  749.     }
  750.     break;
  751.  
  752.   default:
  753.     /* Otherwise, if -sd in effect, format position as a simple degree: */
  754.  
  755.     sprintf(zod, is.fSeconds ? "%11.7f" : "%7.3f", deg);
  756.     break;
  757.   }
  758.   return zod;
  759. }
  760.  
  761.  
  762. /* This is similar to formatting a zodiac degree, but here we return a */
  763. /* string of a (signed) declination value in degrees and minutes.      */
  764.  
  765. char *SzAltitude(deg)
  766. real deg;
  767. {
  768.   static char alt[10];
  769.   int d, m, f;
  770.   real s;
  771.   char ch;
  772.  
  773.   f = deg < 0.0;
  774.   deg = RAbs(deg) + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0);
  775.   d = (int)deg;
  776.   m = (int)(RFract(deg)*60.0);
  777.   ch = us.fAnsi == -1 ? 128 : chDeg1;
  778.   sprintf(alt, "%c%2d%c%02d'", f ? '-' : '+', d, ch, m);
  779.  
  780.   if (is.fSeconds)
  781.   {
  782.     s = RFract(deg)*60.0; s = RFract(s)*60.0;
  783.     sprintf(&alt[7], "%02d\"", (int)s);
  784.   }
  785.   return alt;
  786. }
  787.  
  788.  
  789. /* Here we return a string simply expressing the given value as degrees */
  790. /* and minutes (and sometimes seconds) in the 0 to 360 degree circle.   */
  791.  
  792. char *SzDegree(deg)
  793. real deg;
  794. {
  795.   static char pos[11];
  796.   int d, m;
  797.   real s;
  798.  
  799.   deg = RAbs(deg) + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0);
  800.   d = (int)deg;
  801.   m = (int)(RFract(deg)*60.0);
  802.   sprintf(pos, "%3d%c%02d'", d, chDeg1, m);
  803.   if (is.fSeconds) 
  804.   {
  805.     s = RFract(deg)*60.0; s = RFract(s)*60.0;
  806.     sprintf(&pos[7], "%02d\"", (int)s);
  807.   }
  808.   return pos;
  809. }
  810.  
  811.  
  812. /* Another string formatter, here we return a date string given a month,    */
  813. /* day, and year. We format with the day or month first based on whether    */
  814. /* the "European" date variable is set or not. The routine also takes a     */
  815. /* parameter to indicate how much the string should be abbreviated, if any. */
  816.  
  817. char *SzDate(mon, day, yea, nFormat)
  818. int mon, day, yea, nFormat;
  819. {
  820.   static char szDate[20];
  821.  
  822.   if (us.fEuroDate)
  823.   {
  824.     switch (nFormat) {
  825.     case  2: sprintf(szDate, "%2d %c%c%c%5d", day, chMon3(mon), yea); break;
  826.     case  1: sprintf(szDate, "%d %s %d", day, szMonth[mon], yea);     break;
  827.     case -1: sprintf(szDate, "%2d-%2d-%2d", day, mon, abs(yea)%100);  break;
  828.     default: sprintf(szDate, "%2d-%2d-%4d", day, mon, yea);           break;
  829.     }
  830.   } 
  831.  
  832.   else 
  833.   {
  834.     switch (nFormat) {
  835.     case  3: sprintf(szDate, "%c%c%c %2d, %d", chMon3(mon), day, yea); break;
  836.     case  2: sprintf(szDate, "%c%c%c %2d%5d", chMon3(mon), day, yea);  break;
  837.     case  1: sprintf(szDate, "%s %d, %d", szMonth[mon], day, yea);     break;
  838.     case -1: sprintf(szDate, "%2d/%2d/%2d", mon, day, abs(yea)%100);   break;
  839.     default: sprintf(szDate, "%2d/%2d/%4d", mon, day, yea);            break;
  840.     }
  841.   }
  842.   return szDate;
  843. }
  844.  
  845.  
  846. /* Return a string containing the given time expressed as an hour and */
  847. /* minute quantity. This is formatted in 24 hour or am/pm time based  */
  848. /* on whether the "European" time format flag is set or not.          */
  849.  
  850. char *SzTime(hr, min)
  851. int hr, min;
  852. {
  853.   static char tim[8];
  854.  
  855.   if (us.fEuroTime)
  856.     sprintf(tim, "%2d:%02d", hr, min);
  857.  
  858.   else
  859.     sprintf(tim, "%2d:%02d%cm", Mod12(hr), min, hr < 12 ? 'a' : 'p');
  860.  
  861.   return tim;
  862. }
  863.  
  864.  
  865. /* This just determines the correct hour and minute and calls the above. */
  866.  
  867. char *SzTim(tim)
  868. real tim;
  869. {
  870.   return SzTime(NFloor(tim), (int)(RFract(RAbs(Tim))*100.0+rRound/60.0));
  871. }
  872.  
  873.  
  874. /* Return a string containing the given time zone, given as a real value     */
  875. /* having the hours before GMT in the integer part and minutes fractionally. */
  876.  
  877. char *SzZone(zon)
  878. real zon;
  879. {
  880.   static char tim[7];
  881.  
  882.   sprintf(tim, "%c%d:%02d", zon > 0.0 ? '-' : '+', (int)RAbs(zon), (int)(RFract(RAbs(zon))*100.0+rRound/60.0));
  883.   return tim;
  884. }
  885.  
  886.  
  887. /* Nicely format the given longitude and latitude locations and return    */
  888. /* them in a string. Various parts of the program display a chart header, */
  889. /* and this allows the similar computations to be coded only once.        */
  890.  
  891. char *SzLocation(lon, lat)
  892. real lon, lat;
  893. {
  894.   static char loc[15];
  895.   int i, j;
  896.   char ch;
  897.  
  898.   i = (int)(RFract(RAbs(lon))*100.0+rRound);
  899.   j = (int)(RFract(RAbs(lat))*100.0+rRound);
  900.   ch = us.fAnsi == -1 ? 128 : chDeg1;
  901.  
  902.   sprintf(loc, "%3.0f%c%02d%c%3.0f%c%02d%c",
  903.     RFloor(RAbs(lon)), ch, i, lon < 0.0 ? 'E' : 'W',
  904.     RFloor(RAbs(lat)), ch, j, lat < 0.0 ? 'S' : 'N');
  905.  
  906.   return loc;
  907. }
  908.  
  909.  
  910. #ifdef TIME
  911.  
  912. #ifdef AMIGA
  913. /*
  914.  *  The Amiga system clock knows local mean time (LMT) only.  Time zone settings
  915.  *  are managed via the locale.library and are configurable by the Locale prefs.
  916.  *
  917.  *  Astrolog's GetTimeNow() function expects the time() function to return the
  918.  *  #of seconds since 0 hours, 0 minutes, 0 seconds, January 1, 1970, Coordinated
  919.  *  Universal Time.  My C compilers however all return LMT instead of CUT and so
  920.  *  we need to compute GMT from LMT here.
  921.  */
  922.  
  923. /*global*/ struct Library *LocaleBase;
  924.  
  925. time_t gmtoff(void)
  926. {
  927.   time_t t= 0;  /* the result will hold the #of seconds from GMT */
  928.  
  929.   char *s= getenv("GMTOFF");
  930.  
  931.   /*
  932.    *  We first check the existence of the environment variable `GMTOFF' which can
  933.    *  be used to override the Locale settings.
  934.    */
  935.  
  936.   if(s && *s)
  937.   {
  938.     t= (time_t)atol(s);
  939.     /*printf("GMT Offset (from environment variable `GMTOFF') = %ld second(s)\n", t);*/
  940.   }
  941.  
  942.   else
  943.   {
  944.     /* We try to open locale.library (V38+) in order to get the loc_GMTOffset from there. */
  945.  
  946.     LocaleBase= OpenLibrary("locale.library", 38L);
  947.  
  948.     if(LocaleBase)
  949.     {
  950.       /* We now try to obtain the current locale settings as saved via the Locale prefs. */
  951.  
  952.       struct Locale *loc= OpenLocale(NULL);
  953.  
  954.       if(loc)
  955.       {
  956.         /*
  957.          *  The loc_GMTOffset field contains the offset in minutes of the current location from GMT.
  958.          *  Positive indicates a Westerly direction from GMT, negative Easterly.
  959.          */
  960.  
  961.         t= -60 * loc->loc_GMTOffset;
  962.         CloseLocale(loc);
  963.  
  964.         /*printf("GMT Offset (from `locale.library') = %ld second(s)\n", t);*/
  965.       }
  966.  
  967.       CloseLibrary(LocaleBase);
  968.     }
  969.   }
  970.  
  971.   return t;
  972. }
  973. #endif /* AMIGA */
  974.  
  975. #if 0
  976. void print_time(time_t *t)
  977. {
  978.   struct tm *tp= localtime(t);
  979.  
  980.   printf("tm_sec    = %d;"     "\n", tp->tm_sec);        /* seconds [0,59]                                 */
  981.   printf("tm_min    = %d;"     "\n", tp->tm_min);        /* minutes [0,59]                                 */
  982.   printf("tm_hour   = %d;"     "\n", tp->tm_hour);       /* hours [0,23]                                   */
  983.   printf("tm_mday   = %d;"     "\n", tp->tm_mday);       /* day of month [1,31]                            */
  984.   printf("tm_mon    = %d;"     "\n", tp->tm_mon);        /* month of the year [0,11]                       */
  985.   printf("tm_year   = %d;"     "\n", tp->tm_year);       /* year [0,99] (-1900)                            */
  986.   printf("tm_wday   = %d;"     "\n", tp->tm_wday);       /* days since sunday [0,6]                        */
  987.   printf("tm_yday   = %d;"     "\n", tp->tm_yday);       /* days since jan 1 [0,365]                       */
  988.   printf("tm_isdst  = %d;"     "\n", tp->tm_isdst);      /* nonzero if daylight savings time is in effect  */
  989.   printf("tm_gmtoff = %ld;"    "\n", tp->tm_gmtoff);     /* offset from GMT in seconds                     */
  990.                                                          /* positive values indicating East of Greenwich   */
  991.   printf("tm_zone   = \"%s\";" "\n", tp->tm_zone);       /* abbreviation of timezone name                  */
  992.  
  993.   printf("--> getenv(TZ) = \"%s\"\n", getenv("TZ"));
  994. }
  995. #endif /*0*/
  996.  
  997. /* Compute the date and time it is right now as the program is running      */
  998. /* using the computer's internal clock. We do this by getting the number    */
  999. /* of seconds which have passed since January 1, 1970 and going from there. */
  1000. /* The time return value filled is expressed in the given zone parameter.   */
  1001.  
  1002. void GetTimeNow(mon, day, yea, tim, zon)
  1003. int *mon, *day, *yea;
  1004. real *tim, zon;
  1005. {
  1006.   dword curtimer;  /* time_t */
  1007.   int min, sec;
  1008.   real hr;
  1009.  
  1010.   time(&curtimer);
  1011.  
  1012. #ifdef AMIGA
  1013.   /* The internal clock on Amiga systems knows local mean time only! */
  1014.   curtimer -= gmtoff();  /* LMT -> GMT */
  1015. #endif
  1016.  
  1017.   sec = (int)(curtimer % 60);
  1018.   curtimer /= 60;
  1019.  
  1020.   min = (int)(curtimer % 60);
  1021.   curtimer /= 60;
  1022.  
  1023. #ifdef MAC
  1024.   curtimer += 8;
  1025. #endif
  1026.  
  1027.   hr = (real)(curtimer % 24) - zon;
  1028.  
  1029.   curtimer /= 24;
  1030.  
  1031.   while (hr <   0.0) { curtimer--;  hr += 24.0; }
  1032.   while (hr >= 24.0) { curtimer++;  hr -= 24.0; }
  1033.  
  1034.   curtimer += ldTime;  /* Number of days between 1/1/1970 and 1/1/4713 BC. */
  1035.  
  1036.   JulianToMdy((real)curtimer, mon, day, yea);
  1037.  
  1038.   *tim = hr + (real)min / 100.0 + (real)sec / 6000.0;
  1039.  
  1040.   /*printf("--> GetTimeNow()  mon=%d, day=%d, yea=%d, tim=%f, zon=%f\n", *mon, *day, *yea, *tim, zon);*/
  1041. }
  1042. #endif /* TIME */
  1043.  
  1044.  
  1045. #ifdef PCG
  1046. /* Map one character value to another. This is used in processing special  */
  1047. /* keys and alt key combinations, which are read in from the keyboard as a */
  1048. /* zero immediately followed by some value. This converts that value into  */
  1049. /* something more useful to process and deal with.                         */
  1050.  
  1051. int NFromAltN(nAlt)
  1052. int nAlt;
  1053. {
  1054.   /* Map number pad keys to the numbers characters they correspond to. */
  1055.  
  1056.   if (nAlt == 82)
  1057.     return '0';
  1058.  
  1059.   else if (FBetween(nAlt, 79, 81))
  1060.     return '1' + nAlt - 79;
  1061.  
  1062.   else if (FBetween(nAlt, 75, 77))
  1063.     return '4' + nAlt - 75;
  1064.  
  1065.   else if (FBetween(nAlt, 71, 73))
  1066.     return '7' + nAlt - 71;
  1067.  
  1068.   /* Map F1 through F12 function keys to the values 201-212. */
  1069.  
  1070.   else if (FBetween(nAlt, 59, 68))
  1071.     return 201 + nAlt - 59;
  1072.  
  1073.   else if (FBetween(nAlt, 133, 134))
  1074.     return 211 + nAlt - 133;
  1075.  
  1076.   /* Map Shift+F1 through Shift+F12 keys to the values 213-224. */
  1077.  
  1078.   else if (FBetween(nAlt, 84, 93))
  1079.     return 213 + nAlt - 84;
  1080.  
  1081.   else if (FBetween(nAlt, 135, 136))
  1082.     return 223 + nAlt - 135;
  1083.  
  1084.   /* Map Control+F1 through Control+F12 keys to the values 225-236. */
  1085.  
  1086.   else if (FBetween(nAlt, 94, 103))
  1087.     return 225 + nAlt - 94;
  1088.  
  1089.   else if (FBetween(nAlt, 137, 138))
  1090.     return 235 + nAlt - 137;
  1091.  
  1092.   /* Map Alt+F1 through Alt+F12 keys to the values 237-248. */
  1093.  
  1094.   else if (FBetween(nAlt, 104, 113))
  1095.     return 237 + nAlt - 104;
  1096.  
  1097.   else if (FBetween(nAlt, 139, 140))
  1098.     return 247 + nAlt - 139;
  1099.  
  1100.   return chNull;
  1101. }
  1102. #endif
  1103.  
  1104.  
  1105. /* Given a string representing the complete pathname to a file, strip off    */
  1106. /* all the path information leaving just the filename itself. This is called */
  1107. /* by the main program to determine the name of the Astrolog executable.     */
  1108.  
  1109. char *ProcessProgname(szPath)
  1110. char *szPath;
  1111. {
  1112.   char *b, *c, *e;
  1113.  
  1114.   b = c = szPath;
  1115.  
  1116.   while (*c)
  1117.   {
  1118. #ifdef PC
  1119.     *c = ChUncap(*c);    /* Because DOS filenames are case insensitive. */
  1120. #endif
  1121.     c++;
  1122.   }
  1123.   e = c;
  1124.  
  1125.   while (c > b && *c != '.')
  1126.     c--;
  1127.  
  1128.   if (c > b)
  1129.     *c = 0;
  1130.  
  1131.   else
  1132.     c = e;
  1133.  
  1134.   while (c > b && *c != chDirSep)
  1135.     c--;
  1136.  
  1137.   if (c > b)
  1138.     szPath = c+1;
  1139.  
  1140.   return szPath;
  1141. }
  1142.  
  1143.  
  1144. /* Given a string, return a pointer to a persistent version of it, where  */
  1145. /* 'persistent' means its contents won't be invalidated when the stack    */
  1146. /* frame changes. Strings such as macros, et al, need to be in their own  */
  1147. /* space and can't just be local variables in a function reading them in. */
  1148.  
  1149. char *SzPersist(szSrc)
  1150. char *szSrc;
  1151. {
  1152.   char szT[cchSzDef], *szNew;
  1153.   int cb;
  1154.  
  1155.   /* Some strings such as outer level command line parameter arguments */
  1156.   /* already persist, so we can just return the same string passed in. */ 
  1157.  
  1158.   if (is.fSzPersist)
  1159.     return szSrc;
  1160.  
  1161.   /* Otherwise we make a copy of the string in the local heap and use it. */
  1162.  
  1163.   cb = CchSz(szSrc)+1;
  1164.   AllocateNear(szNew, cb);
  1165.  
  1166.   if (szNew == NULL) 
  1167.   {
  1168.     sprintf(szT, "%s: Not enough near memory for string (%d bytes).", szAppName, cb);
  1169.     PrintWarning(szT);
  1170.   } 
  1171.   else
  1172.     CopyRgb((byte *)szSrc, (byte *)szNew, cb);
  1173.  
  1174.   return szNew;
  1175. }
  1176.  
  1177.  
  1178. /* This is Astrolog's memory allocation routine, returning a pointer given */
  1179. /* a size, a flag for if it is a more than 64K huge allocation, and a      */
  1180. /* string to use when printing an error if the allocation fails.           */
  1181.  
  1182. lpbyte PAllocate(lcb, fHuge, szType)
  1183. long lcb;
  1184. bool fHuge;
  1185. char *szType;
  1186. {
  1187.   char szT[cchSzDef];
  1188.   lpbyte lp;
  1189.  
  1190.   if (fHuge)
  1191.     AllocateHuge(lp, lcb);
  1192.   else
  1193.     AllocateFar(lp, (int)lcb);
  1194.  
  1195. #ifdef PC
  1196.   /* For PC's the array better not cross a segment boundary. */
  1197.  
  1198.   if (lp && !fHuge && WHi(WLo(lp) + lcb) > 0)
  1199.     lp = NULL;
  1200. #endif
  1201.  
  1202.   if (lp == NULL && szType) 
  1203.   {
  1204.     sprintf(szT, "%s: Not enough memory for %s (%ld bytes).",
  1205.       szAppName, szType, lcb);
  1206.     PrintWarning(szT);
  1207.   }
  1208.   return lp;
  1209. }
  1210.  
  1211. /* general.c */
  1212.